﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Xrm.Sdk;
using Microsoft.Crm.Sdk.Messages;
using Microsoft.Xrm.Sdk.Client;
using Microsoft.Xrm.Sdk.Query;

namespace CRM_Solution_Merger
{
    public class CRMSolution
    {
        public Guid SolutionId{ get; set; }
        public string Name { get; set; }
        public string UniqueName { get; set; }
        public bool IsManaged { get; set; }
        public DateTime CreatedOn { get; set; }
        public string Version { get; set; }
        public string PublisherName { get; set; }
        public Guid PublisherId { get; set; }
    }
    public class CRMUtil
    {
        private IOrganizationService _OrgSvc;
        public CRMUtil(IOrganizationService OrgService)
        {
            _OrgSvc = OrgService;
        }

        public List<CRMSolution> RetrieveSolution(bool? IsManaged = null, Guid? solutionId = null)
        {
            List<CRMSolution> solutionList = new List<CRMSolution>();

            QueryExpression query = new QueryExpression("solution");
            query.ColumnSet = new ColumnSet("solutionid", "ismanaged", "friendlyname", "uniquename", "version", "publisherid", "createdon");
            query.Criteria.AddCondition(new ConditionExpression("isvisible", ConditionOperator.Equal, true));
            query.Orders.Add(new OrderExpression("friendlyname", OrderType.Ascending));

            if (IsManaged != null)
            {
                query.Criteria.AddCondition(new ConditionExpression("ismanaged", ConditionOperator.Equal, IsManaged.Value));
            }
            if (solutionId != null)
            {
                query.Criteria.AddCondition(new ConditionExpression("solutionid", ConditionOperator.Equal, solutionId.Value));
            }


            EntityCollection entityCollection = _OrgSvc.RetrieveMultiple(query);

            if (entityCollection != null && entityCollection.Entities.Count > 0)
            {
                foreach (Entity e in entityCollection.Entities)
                {
                    CRMSolution crmSol = new CRMSolution();
                    crmSol.CreatedOn = e.GetAttributeValue<DateTime>("createdon");
                    crmSol.IsManaged = e.GetAttributeValue<bool>("ismanaged");
                    crmSol.Name = e.GetAttributeValue<string>("friendlyname");
                    crmSol.UniqueName = e.GetAttributeValue<string>("uniquename");
                    crmSol.Version = e.GetAttributeValue<string>("version");
                    crmSol.PublisherId = e.GetAttributeValue<EntityReference>("publisherid").Id;
                    crmSol.PublisherName = e.GetAttributeValue<EntityReference>("publisherid").Name;
                    crmSol.SolutionId = e.Id;
                    solutionList.Add(crmSol);
                }
            }
            return solutionList;
        }

        public List<Entity> RetrieveSolutionComponents(Guid solutionId)
        {
            QueryExpression query = new QueryExpression("solutioncomponent");
            query.ColumnSet = new ColumnSet(true);
            query.Criteria.AddCondition("solutionid", ConditionOperator.Equal, solutionId);

            EntityCollection entityCollection = _OrgSvc.RetrieveMultiple(query);

            if (entityCollection != null && entityCollection.Entities.Count > 0)
            {
                return entityCollection.Entities.ToList<Entity>();
            }

            else
            {
                return null;
            }
        }

        public List<KeyValuePair<Guid, string>> RetrievePublisher(Guid? publisherId = null)
        {
            List<KeyValuePair<Guid, string>> publisherList = new List<KeyValuePair<Guid,string>>();

            QueryExpression query = new QueryExpression("publisher");
            query.ColumnSet = new ColumnSet("friendlyname", "publisherid");
            query.Criteria.AddCondition("isreadonly", ConditionOperator.Equal, false);
            query.Criteria.AddCondition("friendlyname", ConditionOperator.NotEqual, "Microsoft Publisher");

            if (publisherId != null)
            {
                query.Criteria.AddCondition("publisherid", ConditionOperator.Equal, publisherId.Value);
            }

            EntityCollection entityCollection = _OrgSvc.RetrieveMultiple(query);

            if (entityCollection != null && entityCollection.Entities.Count > 0)
            {
                foreach(Entity e in entityCollection.Entities)
                {
                    publisherList.Add(new KeyValuePair<Guid,string>(e.Id, e.GetAttributeValue<string>("friendlyname")));
                }
            }
           return publisherList;
        }

        private List<Entity> MergeComponents(List<List<Entity>> componentList)
        {
            List<Entity> MergedComponentList = new List<Entity>();

            foreach (List<Entity> entityList in componentList)
            {
                if (entityList != null)
                {
                    foreach (Entity e in entityList)
                    {
                        if (e != null && !MergedComponentList.Any(a => a.Id == e.Id))
                        {
                            MergedComponentList.Add(e);
                        }
                    }
                }
            }
            return MergedComponentList;
        }

        public void MergeSolutions(CRMSolution newSolution,List<Guid>solutionIds)
        {
            List<List<Entity>> componentList = new List<List<Entity>>();

            foreach (Guid solId in solutionIds)
            {
                componentList.Add(RetrieveSolutionComponents(solId));
            }
            //List<Entity> newSolutionComponents = new List<Entity>();
            if (newSolution.SolutionId != Guid.Empty)
            {
                componentList.Add(RetrieveSolutionComponents(newSolution.SolutionId));
            }

            List<Entity> mergedComponents = MergeComponents(componentList);
            string un = string.IsNullOrEmpty(newSolution.Name) ? newSolution.UniqueName : newSolution.Name;
            newSolution.UniqueName = un.Replace(" ", "");

            if (newSolution.SolutionId == Guid.Empty)
            {
                Entity solutionEntity = new Entity("solution");
                solutionEntity["friendlyname"] = newSolution.Name;
                solutionEntity["uniquename"] = newSolution.UniqueName;
                solutionEntity["publisherid"] = new EntityReference("publisher", newSolution.PublisherId);
                solutionEntity["description"] = string.Empty;
                solutionEntity["version"] = "1.0";
                _OrgSvc.Create(solutionEntity);
            }

            foreach (Entity component in mergedComponents)
            {
                AddSolutionComponentRequest addReq = new AddSolutionComponentRequest()
                {
                    ComponentType = component.GetAttributeValue<OptionSetValue>("componenttype").Value,
                    ComponentId = component.GetAttributeValue<Guid>("objectid"),
                    SolutionUniqueName = newSolution.UniqueName
                };
                _OrgSvc.Execute(addReq);
            }
        }
    }
}
